package cc.rengu.igas.opms.core.service.base;

import cc.rengu.igas.opms.common.constant.OpmsTreeNodeConstant;
import cc.rengu.igas.opms.common.constant.OpmsParamConstant;
import cc.rengu.igas.opms.common.enums.RespCodeEnum;
import cc.rengu.igas.share.common.constant.UserSessionConstant;
import cc.rengu.igas.share.core.realize.PublicService;
import cc.rengu.igas.share.core.realize.impl.PublicServiceImpl;
import cc.rengu.igas.share.core.model.MchntInfo;
import cc.rengu.igas.share.core.model.PlatMchntInfo;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.UserService;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.igas.share.core.realize.impl.RiskMonitorServiceImpl;
import cc.rengu.igas.share.core.realize.impl.UserServiceImpl;
import cc.rengu.oltp.service.base.BizBaseService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.MchntOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.PayOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.UserOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.MchntOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.PayOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.UserOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.MchntOrderInfo;
import cc.rengu.oltp.service.common.entity.PayOrderInfo;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.common.entity.UserOrderInfo;
import cc.rengu.oltp.service.common.enums.BizStatusEnum;
import cc.rengu.oltp.service.common.enums.TransStatusEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.RiskMonitorInfo;
import cc.rengu.oltp.service.realize.RiskMonitorService;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.annotation.BIZ_AFTER;
import cc.rengu.oltp.utility.annotation.BIZ_BEFORE;
import cc.rengu.oltp.utility.annotation.BIZ_PROCESS;
import cc.rengu.oltp.utility.util.*;


import java.util.List;

/**
 * 线上支付服务基类
 * Created by 王成 on 2018/4/17.
 */
public abstract class PayService extends BizBaseService {
    public PlatMchntInfo platMchntInfo = null;
    public MchntInfo mchntInfo = null;

    @Override
    protected Object beforeBizProcess() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String txnNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_NUM);
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        String msgSrcId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.MSG_SRC_ID);

        /* Step1：查找BIZ_BEFORE的注解*/
        invokeBizAnnoMethod(BIZ_BEFORE.class);

        /* Step2：检查验签状态 */
        String verifySignFlag = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.VERIFY_SIGN_FLAG);
        if (AppParamConstant.NO.equals(verifySignFlag)) {
            throw new BizException(RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode(), RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
        }

        /* Step3：检查接口合法性 */
        PublicService publicService = new PublicServiceImpl();
        publicService.checkInputValidity(msgSrcId, txnNum);
        publicService.checkInputParamLegal();

        /* Step4：检查平台商户状态 */
        MchntService mchntService = new MchntServiceImpl();
        String platMchntNo = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.PLAT_MCHNT_NO);
        if (!StringUtil.isEmptyOrNull(platMchntNo)) {
            /* 校验交易接口中的商户号与会话中的商户号是否一致 */
            UserService userService = new UserServiceImpl();
            String transMchntNo = userService.getUserSessionValue(UserSessionConstant.PLAT_MCHNT_NO);
            if (!StringUtil.isEmptyOrNull(transMchntNo) && !platMchntNo.equals(transMchntNo)) {
                rglog.error("交易请求上送的商户<{}>与用户会话中商户号<{}>不一致!", platMchntNo, transMchntNo);
                throw new BizException(RespCodeEnum.MCHNT_DISACCORD.getRespCode(), RespCodeEnum.MCHNT_DISACCORD.getRespDesc());
            }
            if (!mchntService.checkPlatMchntStatus(instId, platMchntNo)) {
                rglog.error("平台商户<{}>状态异常!", platMchntNo);
                throw new BizException(RespCodeEnum.MCHNT_STATUS_ABNORMAL.getRespCode(), RespCodeEnum.MCHNT_STATUS_ABNORMAL.getRespDesc());
            }
            platMchntInfo = mchntService.getPlatMchntInfo(instId, platMchntNo);
            if (null == platMchntInfo) {
                rglog.error("查询平台商户基本信息失败<{}>", platMchntNo);
                throw new BizException(RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespDesc());
            }
        }

        /* Step5：检查商户合法性 */
        String mchntNo = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.MCHNT_NO);
        if (!StringUtil.isEmptyOrNull(mchntNo)) {
            if (!mchntService.checkMchntStatus(instId, mchntNo)) {
                rglog.error("商户<{}>状态异常!", mchntNo);
                throw new BizException(RespCodeEnum.MCHNT_STATUS_ABNORMAL.getRespCode(), RespCodeEnum.MCHNT_STATUS_ABNORMAL.getRespDesc());
            }
            mchntInfo = mchntService.getMchntInfo(instId, mchntNo);
            if (null == mchntInfo) {
                rglog.error("查询商户基本信息失败<{}>", mchntNo);
                throw new BizException(RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespDesc());
            }
        }

        /* Step6：交易处理前客户化 */
        rglog.info("交易处理前客户化");
        return bizBeforeCust();
    }

    @Override
    protected BizResponse callBizService(Object request) throws Exception {
        /* Step1：公共业务处理 */

        /* Step2：查找BIZ_PROCESS的注解 */
        invokeBizAnnoMethod(BIZ_PROCESS.class);

        /* Step3：业务逻辑处理*/
        return bizProcessCust(request);
    }

    @Override
    protected void afterBizProcess(BizResponse response) throws Exception {
        /* Step1：业务调用后公共处理*/
        genGetCouponToken();

        /* Step2：查找BIZ_AFTER的注解 */
        invokeBizAnnoMethod(BIZ_AFTER.class);

        /* Step3：业务调用后客户化处理 */
        bizAfterCust(response);
    }

    @Override
    protected RiskMonitorInfo getRiskMonitorInfo() {
        RiskMonitorInfo riskMonitorInfo = new RiskMonitorInfo();
        RiskMonitorService riskMonitorService = new RiskMonitorServiceImpl();
        riskMonitorInfo.setTransMonitor(riskMonitorService);
        return riskMonitorInfo;
    }

    /**
     * 业务处理前客户化处理
     *
     * @return 业务处理实体类
     */
    protected abstract Object bizBeforeCust() throws Exception;

    /**
     * 业务逻辑处理客户化处理
     *
     * @param request 业务处理前的返回实体类
     * @return 业务处理结果
     */
    protected abstract BizResponse bizProcessCust(Object request) throws Exception;

    /**
     * 业务处理后逻辑处理
     *
     * @param response 业务调用响应信息
     */
    protected abstract void bizAfterCust(Object response) throws Exception;

    /**
     * 支付成功后设置领取卡券的认证TOKEN
     */
    protected void genGetCouponToken() {

        try {
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            String sessionId = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.SESSION);
            String instId = xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.INST_ID);

            SysParamService sysParamService = new SysParamServiceImpl();
            SysParam sysParam = sysParamService.getSysParamInfo(instId, OpmsParamConstant.OPMS_SYS_PARAM, OpmsParamConstant.SESSION_EXPIRY_TIMES);
            int sessionExpiryTime = (sysParam == null || StringUtil.isEmptyOrNull(sysParam.getParamValue())) ? OpmsParamConstant.SESSION_DEFAULT_TIMES : Integer.parseInt(sysParam.getParamValue());

            String key = OpmsParamConstant.GET_COUPON_TOKEN_KEY + sessionId;
            rglog.info("设置领取优惠券标识的key:<{}>", key);
            RedisUtil.onceSet(key, AppParamConstant.YES, sessionExpiryTime);
        } catch (Exception e) {
            rglog.error("支付成功后设置领取优惠券的认证token异常:<{}>", e);
        }
    }

    protected boolean updateOrderInfo(UserOrderInfo userOrderInfo, List<MchntOrderInfo> mchntOrderInfoList, PayOrderInfo payOrderInfo) throws Exception {
        int iReturnCode;
        String dbPoolName = XmlConfigUtil.getXmlConfigCacheKeyValue(AppParamConstant.DATABASE_CONF_NODE, AppParamConstant.OLTP_POOL_NAME);
        DbsUtil dbsUtil = new DbsUtil(dbPoolName);
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String bizStatus = userOrderInfo.getBizStatus();
        rglog.debug("更新用户订单/商户订单/支付订单，当前用户订单业务状态:<{}>", bizStatus);
        userOrderInfo.setSysSeqNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SYS_SEQ_NUM));
        userOrderInfo.setTransStatus(TransStatusEnum.TRANS_INIT.getStatus());
        userOrderInfo.setBizStatus(BizStatusEnum.PROCESSING.getStatus());
        userOrderInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        userOrderInfo.setTxnNum(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM));
        userOrderInfo.setUserId(xmlTreeUtil.getXmlTreeStringValue(OpmsTreeNodeConstant.USER_ID));
        /* Step1：开启数据库事务 */
        String corporation = xmlTreeUtil.getXmlTreeStringValue(AppParamConstant.CORPORATION);
        if (null != corporation && !corporation.isEmpty()) {
            String dbPoolCashNode = corporation.trim() + AppParamConstant.DATABASE_CONF_NODE;
            dbPoolName = XmlConfigUtil.getXmlConfigCacheKeyValue(dbPoolCashNode, AppParamConstant.OLTP_POOL_NAME);
            dbsUtil = new DbsUtil(dbPoolName);
        }
        dbsUtil.dbsBeginTransaction();
        /* Step2：更新用户订单信息 */
        UserOrderInfoMapper userOrderInfoMapper = new UserOrderInfoMapperImpl();
        iReturnCode = userOrderInfoMapper.updateUserOrderInfoByUserOrderId(userOrderInfo);
        if (0 != iReturnCode) {
            dbsUtil.dbsEndTransaction(false);
            return false;
        }
        /* Step3：更新商户订单信息 */
        if (null != mchntOrderInfoList && !mchntOrderInfoList.isEmpty()) {
            MchntOrderInfoMapper mchntOrderInfoMapper = new MchntOrderInfoMapperImpl();
            for (MchntOrderInfo mchntOrderInfo : mchntOrderInfoList) {
                if (BizStatusEnum.INIT.getStatus().equals(bizStatus) || bizStatus == null) {
                    rglog.debug("当前订单为初始订单，直接更新对应商户订单");
                    /*新建订单时更新商户订单*/
                    iReturnCode = mchntOrderInfoMapper.updateMchntOrderInfoByMchntOrderId(mchntOrderInfo);
                } else {
                    rglog.debug("当前订单不是初始订单，需要插入新的商户订单");
                    /*对客营销金额赋值为空*/
                    mchntOrderInfo.setCustomerMarketAmt("0.00");
                    /*商户清算本金=商户订单金额-订单优惠金额*/
                    mchntOrderInfo.setRealSettleAmt(AmountUtil.subtraction(mchntOrderInfo.getOrderAmt(), mchntOrderInfo.getOrderDiscountAmt()));
                    /*失败后支付的插入新的订单*/
                    mchntOrderInfo.setTxnDate(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE));
                    mchntOrderInfo.setTxnTime(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME));
                    iReturnCode = mchntOrderInfoMapper.insertMchntOrderInfo(mchntOrderInfo);
                }
                if (0 != iReturnCode) {
                    dbsUtil.dbsEndTransaction(false);
                    return false;
                }
            }
        }
        /* Step4：更新支付订单信息 */
        if (null != payOrderInfo) {
            PayOrderInfoMapper payOrderInfoMapper = new PayOrderInfoMapperImpl();
            iReturnCode = payOrderInfoMapper.updatePayOrderInfoByPrimaryKey(payOrderInfo);
            if (0 != iReturnCode) {
                dbsUtil.dbsEndTransaction(false);
                rglog.error("更新支付订单信息失败,sysSeqNum:<{}>,mchntOrderId:<{}>,channelId:<{}>,realPayAmt:<{}>,channelFee:<{}>,settleDate:<{}>,channelSeqNum:<{}>,iReturnCode:<{}>",
                        payOrderInfo.getSysSeqNum(), payOrderInfo.getMchntOrderId(), payOrderInfo.getMsgDstId(), payOrderInfo.getRealPayAmt(), payOrderInfo.getChannelFee(), payOrderInfo.getSettleDate(), payOrderInfo.getChannelSeqNum(), iReturnCode);
                return false;
            }
        }
        /* Step5：结束数据库事务 */
        dbsUtil.dbsEndTransaction(true);
        return true;
    }

}
